package com.huitone.smspfm.socket.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.bin.common.utils.DateTimeUtils;
import com.bin.common.utils.StringUtils;
import com.huitone.smspfm.socket.protocol.sgip.constant.CommandID;
import com.huitone.smspfm.socket.protocol.sgip.message.Message;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.ActiveTest;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.ActiveTestResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Bind;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.BindResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Deliver;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.DeliverResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Report;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Report2;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Report2Resp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.ReportResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit2;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit2Resp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.SubmitResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.UnBind;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.UnBindResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msghead.Head;

/**   
* @Title SocketUtils.java 
* @Package com.huitone.smspfm.socket.client 
* @Description TODO(用一句话描述该文件做什么) 
* @author ybliang   
* @date 2017年3月30日 下午3:38:26 
* @version V1.0   
*/
public class SocketUtils {
	
	private static final Logger logger = LogManager.getLogger(SocketUtils.class);
	
	public static Message read(InputStream inputStream) throws IOException {
		
		long startTime = System.currentTimeMillis();
		
		Head head = null;
		byte[] body = null;
		
		List<Byte> temp = new ArrayList<Byte>();
		
		ArrayBlockingQueue<Byte> queue = new ArrayBlockingQueue<Byte>(Head.HEAD_LENGTH);
		
		int index = 0;
		
		while(true) { // 读取消息头(程序不断循环直到读到正确的消息头)
			
			try {
				byte b = (byte) inputStream.read();
				temp.add(b);
				queue.put(b);
			} catch (InterruptedException e) {
				break;
			} catch (SocketTimeoutException e) {
				
				if (!temp.isEmpty()) {
					logger.info("错误的消息包:" + temp.toString());
				}
				throw e;
			}
			index ++;
			
			if (index == Head.HEAD_LENGTH) {
				
				boolean disconnected = true;
				
				int n = 0;
				byte[] bytes = new byte[Head.HEAD_LENGTH];
				
				Iterator<Byte> iterator = queue.iterator();
				while(iterator.hasNext() && n < Head.HEAD_LENGTH) {
					byte bytz = (byte) iterator.next();
					bytes[n] = bytz;
					n ++;
					if (bytz != -1) {
						disconnected = false;
					}
				}
				
				if (disconnected) {
					throw new SocketException("Socket disconnected");
				}
				
				head = Head.build(bytes);
				if (!CommandID.hasCommandID(head.getCommandID())) {
					try {
						queue.take();
					} catch (InterruptedException e) {
						break;
					}
					index --;
				} else {
					break;
				}
			}
		}
		
		if (head != null) {
			
			int bodyLength = head.getMessageLength() - Head.HEAD_LENGTH;
			
			body = new byte[bodyLength];
			
			for (int i = 0 ; i < bodyLength ; i ++) {
				body[i] = (byte) inputStream.read();
			}
			
			return buildMessage(head, body);
		} else {
			logger.error("[空消息头错误]");
		}
		
		long endTime = System.currentTimeMillis();
		
		long time = endTime - startTime;
		
		logger.debug("读取一条消息耗时:" + time + "ms");
		
		return null;
	}
	
	public static boolean write(byte[] bytes, OutputStream outputStream) throws IOException {
		
		boolean writed = true;
		
		if (outputStream != null && bytes != null) {
			outputStream.write(bytes);
			outputStream.flush();
		} else {
			writed = false;
		}
		
		return writed;
	}
	
	public static boolean isConnected(Socket socket) {
		
		boolean result = true;
		
		if (socket == null || socket.isClosed()) {
			result = false;
		} else {
			
			ActiveTest activeTest = new ActiveTest();
			
			Head head = Head.build(activeTest.length(), CommandID.SGIP_ACTIVE_TEST, 100000, StringUtils.toIntegerP(DateTimeUtils.dateToStr(new Date(), "MMddHHmmss")), SerialUtils.getSerial());
			
			try {
				write(Message.build(head, activeTest), socket.getOutputStream());
			} catch (Exception e) {
				result = false;
			}
		} 
		
		return result;
	}
	
	private static Message buildMessage(Head msgHead, byte[] body) {
		
		switch (CommandID.getCommandIDem(msgHead.getCommandID())) {
		
		case SGIP_BIND:
			
			return new Message(msgHead, Bind.build(body));
			
		case SGIP_BIND_RESP:
			
			return new Message(msgHead, BindResp.build(body));
			
		case SGIP_SUBMIT:
			
			return new Message(msgHead, Submit.build(body));
			
		case SGIP_SUBMIT_RESP:
			
			return new Message(msgHead, SubmitResp.build(body));
			
		case SGIP_DELIVER:
			
			return new Message(msgHead, Deliver.build(body));
			
		case SGIP_DELIVER_RESP:
			
			return new Message(msgHead, DeliverResp.build(body));
			
		case SGIP_REPORT:
			
			return new Message(msgHead, Report.build(body));
			
		case SGIP_REPORT_RESP:
			
			return new Message(msgHead, ReportResp.build(body));
			
		case SGIP_UNBIND:
			
			return new Message(msgHead, UnBind.build(body));
			
		case SGIP_UNBIND_RESP:
			
			return new Message(msgHead, UnBindResp.build(body));
			
		case SGIP_ACTIVE_TEST:
			
			return new Message(msgHead, ActiveTest.build(body));
			
		case SGIP_ACTIVE_TEST_RESP:
			
			return new Message(msgHead, ActiveTestResp.build(body));
	
		case SGIP_SUBMIT2:
			
			return new Message(msgHead, Submit2.build(body));
			
		case SGIP_SUBMIT2_RESP:
			
			return new Message(msgHead, Submit2Resp.build(body));
			
		case SGIP_REPORT2:
			
			return new Message(msgHead, Report2.build(body));
			
		case SGIP_REPORT2_RESP:
			
			return new Message(msgHead, Report2Resp.build(body));
			
		default:
			
			logger.error("[消息头信息错误]" + msgHead.toString());
			
			return null;
		}
	}
}
